package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author luhan
 * @create 2022/5/21 21:02
 */
@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${authUrls.url}")
    private String authUrls;

    // 匹配路径的工具类
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 需要知道用户访问的URL
        ServerHttpRequest request = exchange.getRequest();
        // request.getURI().getPath() = list.html?category3Id=61
        String path = request.getURI().getPath();
        // 判断当前path属于哪一种 /**/inner/**/ 属于内部响应接口！需要做出响应！
        if (antPathMatcher.match("/**/inner/**",path)){
            // 获取响应对象
            ServerHttpResponse response = exchange.getResponse();
            // 不能继续走
            return out(response, ResultCodeEnum.PERMISSION);
        }

        // 获取到用户id就是登录了，否者没有登录
        String userId = getUserId(request);
        if ("-1".equals(userId)){
            // 获取响应对象
            ServerHttpResponse response = exchange.getResponse();
            // 不能继续走
            return out(response, ResultCodeEnum.PERMISSION);
        }


        // 判断用户是否访问了trade.html,myOrder.html 这样的控制器时，必须登录
        // authUrls=trade.html,myOrder.html,list.html
        String[] split = authUrls.split(",");
        for (String url : split) {
            // 当前path包含以上控制器地址
            // 用户未登录且访问的控制器时必须登录的
            if (path.indexOf(url)!=-1 && StringUtils.isEmpty(userId)){
                // 需要跳转到登录页面
                ServerHttpResponse response = exchange.getResponse();
                // 做一些设置
                // 303状态码表示由于请求对应的资源存在着另一个URI，应使用重定向获取请求的资源
                response.setStatusCode(HttpStatus.SEE_OTHER);
                // request.getURI() = http://list.gmall.com/list.html?category3Id=61
                response.getHeaders().set(HttpHeaders.LOCATION,"http://www.gmall.com/login.html?originUrl="+request.getURI());
                // 重定向
                return response.setComplete();
            }
        }

        // /api/**/auth/** 需要登录
        if (antPathMatcher.match("/api/**/auth/**",path)){
            // 判断当前是否登录
            if (StringUtils.isEmpty(userId)){
                // 做出响应
                ServerHttpResponse response = exchange.getResponse();
                // 不能继续走
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        // 验证通过之后：将用户的id传递给后台微服务
        if (!StringUtils.isEmpty(userId)){
            // 需要将用户id放进求头中中！
            request.mutate().header("userId", userId).build();

            return chain.filter(exchange.mutate().request(request).build());
        }
        // 默认返回
        return chain.filter(exchange);
    }

    /**
     * 响应方法
     * @param response
     * @param resultCodeEnum
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        // 提示的数据ResultCodeEnum中的message
        Result result = Result.build(null,resultCodeEnum);
        // 需要将result输出到页面 变成JSON字符串
        String jsonString = JSON.toJSONString(result);
        DataBuffer wrap = response.bufferFactory().wrap(jsonString.getBytes());
        // 使用响应对象设置响应头
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        // 输出到页面
        return response.writeWith(Mono.just(wrap));
    }

    /**
     * 获取用户id
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        String token = "";
        // 用户id存储在缓存中！token在cookie，header中
        List<String> stringList = request.getHeaders().get("token");
        if (!CollectionUtils.isEmpty(stringList)){
            token = stringList.get(0);
        }else {
            // 如果header为空，从cookie中获取
            HttpCookie httpCookie = request.getCookies().getFirst("token");
            if (httpCookie!=null){
                token = httpCookie.getValue();
            }
        }


        // 组成缓存中的key
        String userKey = "user:login:"+token;
        String object = (String) redisTemplate.opsForValue().get(userKey);
        if (!StringUtils.isEmpty(object)){
            // 本质：jsonObject
            JSONObject jsonObject = JSON.parseObject(object, JSONObject.class);
            // 校验 获取当前客户端的ip
            String cuurIp = IpUtil.getGatwayIpAddress(request);
            String ip = (String) jsonObject.get("ip");
            if (cuurIp.equals(ip)){
                String userId = (String) jsonObject.get("userId");
                return userId;
            } else {
                return "-1";
            }
        }else {
            return "";
        }

    }
}
